

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

#include "soc.h"




/*
 *  incr = 0, get curr postion.
 *  incr > 0, alloc.
 *  ince < 0, free.
 */
extern char __BSS_END__[];
void * _sbrk( intptr_t incr )
{
	static intptr_t  heapos = (intptr_t)__BSS_END__;
	void * prepos = (void *)heapos;
	
	if ( incr > 0 )  {
		/* judge range, err for return (void *)(-1).. */
		heapos += incr;
	} else {
		heapos += incr;
	}
	
	/**/
	return prepos;
}





/*
默认启动 BYP 使能, 所以 C906B 使用 xtal-25Mhz 时钟频率.
切换 C906B 时钟, 测试.
*/

int cpu_clock( void )
{
	clk_div_regs_t * pdiv = (clk_div_regs_t *)CLK_DIV_BASE;
	uint32_t temp;

	/* 
	* DIV_C906_B1 : 1500/3 = 500Mhz
	* [20:16] : divider factor
	*   [9:8] : 0-src fpll
	*     [3] : 0-div from default, 1-div from [20:16]
	*     [0] : 0-Reset, 1-Running
	*/
	pdiv->DIV_C906_B1 = 0x30009;

	/*
	*  [24] : C906C, 0-select DIV_C906_C1, 1-select DIV_C906_C0;
	*  [23] : C906B, 0-select DIV_C906_B1, 1-select DIV_C906_B0;
	*   [0] : A53A, 0-select DIV_A53_A1, 1-select DIV_A53_A0;
	*/
	pdiv->CLK_SEL = 0;
	test_sleep( 100 );

	/*
	*  [9] : clk_src_vip_sys_4
	*  [8] : clk_src_vip_sys_3
	*  [7] : C906C
	*  [6] : C906B
	*  [5] : clk_src_rtc_sys
	*  [4] : clk_ap_debug
	*  [3] : clk_vc_src
	*  [2] : clk_aud_src
	*  [1] : clk_src_vip_sys_2
	*/
	temp = pdiv->CLK_BYP[1];
	pdiv->CLK_BYP[1] = temp & 0xFBF;

	
	/*
	clk_cpu_axi0, SRC = fpll
	: 1500/2 = 750Mhz
	*/
	pdiv->DIV_CPU_AXI0 = 0x20009;

	/* AXI4 : 1500/5 = 300Mhz
	*  [20:16] : divider factor
	*    [9:8] : 0-src fpll, 1-src disppll
	*      [3] : 0-div from default, 1-div from [20:16]
	*      [0] : 0-Reset, 1-Running
	*/
	pdiv->DIV_AXI4 = 0x50009;

	/*
	* AXI6 : fix src fpll : 1500/5 = 300Mhz
	*  [20:16] : divider factor
	*      [3] : 0-div from default, 1-div from [20:16]
	*      [0] : 0-Reset, 1-Running
	*/
	pdiv->DIV_AXI6 = 0x50009;


	/*
	*  [20] : clk_axi6
	*  [19] : clk_axi4
	*   [1] : clk_cpu_axi0
	*/
	temp = pdiv->CLK_BYP[0];
	pdiv->CLK_BYP[0] = temp & 0xFFE7FFFD;
	return 0;
}



void  SystemInit( void )
{
	/* cpu config */
	cpu_clock();

	/* intc config */
	for ( int i=0; i<128; i++ ) {
		csi_vic_disable_irq( i );
	}

	for ( int i=1; i<128; i++ ) {
		csi_vic_set_prio( i, 0 );
	}

	/* pmp config */

	/* board config */

	/* printf config */

	return;
}


int sys_reset( void )
{
	uint32_t temp;

	write_reg( RTC_CORE_BASE + RTC_EN_WARM_RST_REQ, 1 );

	while ( true )  {
		temp = read_reg( RTC_CORE_BASE + RTC_EN_WARM_RST_REQ );
		if ( temp == 1 )  {
			break;
		}
	}

	printf( "enable..\n");

	/**/
	temp = read_reg( RTC_CTRL_BASE + RTC_CTRL0 );
	write_reg( RTC_CTRL_BASE + RTC_CTRL0_UNLOCKKEY, 0xAB18 );
	write_reg( RTC_CTRL_BASE + RTC_CTRL0, temp | 0xFFFF0810 );

	printf( "request..\n");
	
	/**/
	while ( true )  {
		__NOP();
		__NOP();
	}

	return 0;
}


typedef struct _tag_stack_regs  {

	uintptr_t ra;			/* x1, ra : return addr.. */
	uintptr_t gp;
	uintptr_t tp;
        uintptr_t rsvd;

	uintptr_t args[8];		/* a0 ... a7 */
	uintptr_t tmps[7];		/* t0 ... t6 */

} stack_regs_t;


extern void usbd_try_send( void * ptr, int tlen );
extern void * upk_alloc( void );

void trap_handler( uint64_t mcus, stack_regs_t * pregs )
{
	uint64_t mexppc;
	uint64_t mcause;
	uint64_t mtval;

	mexppc = __get_MEPC();
	mcause = __get_MCAUSE();
	mtval = __get_MTVAL();

	if ( mcause == 11 )  {
		
		/* sub proc */
		if ( pregs->args[7] == 123 )  {

			// send pkt to usb.
			usbd_try_send( (void *)(intptr_t)pregs->args[0], (int)(intptr_t)pregs->args[1] );
			
		} else if (pregs->args[7] == 134 )  {

			void * ptr;
			
			ptr = upk_alloc();
			pregs->args[0] = (uintptr_t)ptr;

		} else {
			printf( "todo : a7 = %lx\n", pregs->args[7] );
		}
		
		__set_MEPC( mexppc + 4 );
		return;
	}

	printf( "panic haldler...\n" );
	
	printf( "0x%X:%x...\n", (uint32_t)(mcause >> 32), (uint32_t)mcause );
	printf( "0x%X:%x...\n", (uint32_t)(mexppc >> 32), (uint32_t)mexppc );
	printf( "0x%X:%x...\n", (uint32_t)(mtval >> 32), (uint32_t)mtval );
	printf( "0x%X:%x...\n", (uint32_t)(pregs->ra >> 32), (uint32_t)pregs->ra );

	while ( true )  {
		asm volatile ( "nop" );
		asm volatile ( "nop" );
	}
}

extern void usbd_int_handler( void );


void intr_handler( uint32_t excpid )
{
	uint32_t irqn;
	uint32_t temp;
	PLIC_Type *plic = (PLIC_Type *)CONFIG_PLIC_BASE;
	dw_timer_regs_t * ptmr = (dw_timer_regs_t *)CVI_TIMER0_BASE;

	// printf( "excpid : %u\n", excpid );

	if ( excpid == 11 )  {

		irqn = plic->PLIC_H0_MCLAIM;

		if ( irqn == 30 )  {

			// usb.
			usbd_int_handler();

		} else if ( irqn == 79 )  {
			
			// clear timer-0
			temp = ptmr->TEOI;
			printf( "timer-0, %u\n", temp );

		} else {

			printf( "todo : other irq = %u\n", irqn );
		}

		//
		plic->PLIC_H0_MCLAIM = irqn;
	}

	return;
}

